package spring.bean;

/**
 * @author cg
 * @date 2023/6/14 14:59
 */

import spring.annotation.*;
import spring.annotation.bean.*;
import spring.utils.BeanUtils;

import java.io.File;
import java.lang.annotation.Documented;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

import static spring.utils.BeanUtils.getDefaultBeanName;

/**
 * 解析spring配置类
 */
public class BeanDefinitionReader {

    /**
     * 扫描到的所有class文件名集合
     */
    private List<String> registClassNameList = new ArrayList<>();

    public BeanDefinitionReader(Class<?> configClazz) {
        //加载配置类信息，找到所有的class文件
        load(configClazz);
//        System.out.println("registClassNameList = " + registClassNameList);
    }

    /**
     * 解析配置类
     *
     * @param configClazz 配置类
     */
    private void load(Class<?> configClazz) {
        //解析componentScan注解
        ComponentScan componentScan = configClazz.getAnnotation(ComponentScan.class);
        String componentScanPath = componentScan.value();
        //默认获取当前类下的包
        if ("".equals(componentScanPath)) {
            componentScanPath = configClazz.getName();
            componentScanPath = componentScanPath.substring(0, componentScanPath.lastIndexOf("."));
        }
        //找到当前包的绝对路径
        ClassLoader classLoader = configClazz.getClassLoader();
        URL resource = classLoader.getResource(componentScanPath.replaceAll("\\.", "/"));
        String dirPath = Objects.requireNonNull(resource).getFile();
//        System.out.println("dirPath = " + dirPath);
        File dir = new File(dirPath);
        if (dir.isDirectory()) {
            //扫描
            doScan(dir, componentScanPath);
        } else {
            throw new RuntimeException("解析包路径失败");
        }
    }

    /**
     * 扫描包下的所有className并存入list中
     *
     * @param dir 包文件
     */
    private void doScan(File dir, String packageName) {
        if (dir.isFile()) {
            //判断是否是class文件
            if (dir.getName().endsWith(".class")) {
                String dirName = dir.getName();
                registClassNameList.add(packageName + "." + (dirName.substring(0, dirName.lastIndexOf("."))));
            }
            return;
        }
        for (File file : Objects.requireNonNull(dir.listFiles())) {
            if (file.isDirectory()) {
                doScan(file, packageName + "." + file.getName());
            } else {
                doScan(file, packageName);
            }
        }
    }

    /**
     * 封装所有class文件为beanDefinition对象
     *
     * @return beanDefinition集合
     */
    public Map<String, BeanDefinition> loadBeanDefinitions() {
        //遍历所有的className
        Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
        for (String className : registClassNameList) {
            try {
                Class<?> clazz = Class.forName(className);
                //解析bean类型的注解
                //注解和接口不需要解析
                if (clazz.isAnnotationPresent(Documented.class)) continue;
                for (Class<?> clazzInterface : clazz.getInterfaces()) {
                    doLoad(beanDefinitionMap, clazzInterface);
                }
                doLoad(beanDefinitionMap, clazz);
            } catch (ClassNotFoundException e) {
                throw new RuntimeException("未找到" + className + "类");
            }
        }
        return beanDefinitionMap;
    }

    private void doLoad(Map<String, BeanDefinition> beanDefinitionMap, Class<?> clazz) {
        // spring中用到了AliasFor来巧妙解决不同注解实现相同功能的问题，这里只采用了if-else
        String beanName = "";
        if (clazz.isAnnotationPresent(Component.class)) {
            Component component = clazz.getAnnotation(Component.class);
            beanName = component.value();
            if ("".equals(beanName)) {
                beanName = getDefaultBeanName(clazz);
            }
        }else if (clazz.isAnnotationPresent(Controller.class)) {
            Controller controller = clazz.getAnnotation(Controller.class);
            beanName = controller.value();
            if ("".equals(beanName)) {
                beanName = getDefaultBeanName(clazz);
            }
        }else if (clazz.isAnnotationPresent(Service.class)){
            Service service = clazz.getAnnotation(Service.class);
            beanName = service.value();
            if ("".equals(beanName)) {
                beanName = getDefaultBeanName(clazz);
            }
        }
        if (!"".equals(beanName)) {
            BeanDefinition beanDefinition = new BeanDefinition(clazz, beanName);
            //填充scope和lazy
            if (clazz.isAnnotationPresent(Lazy.class)) {
                beanDefinition.setLazy(true);
            }
            if (clazz.isAnnotationPresent(Scope.class)) {
                beanDefinition.setSingleton(false);
            }
            beanDefinitionMap.put(beanName, beanDefinition);
        }
    }

}
